package com.zodiac.app.szse.d.d0602.d060201;

import com.github.lzm320a99981e.component.dataautomation.DataAutomationTemplate;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.zodiac.app.szse.configuration.CustomizeApplicationProperties;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class D060201Service {
    private final ListeningExecutorService extractorExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(50));
    private final ListeningExecutorService downloadExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(50));
    private final ListeningExecutorService convertToHtmlExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(20));
    private final ListeningExecutorService convertToTextExecutorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(20));

    @Autowired
    private CustomizeApplicationProperties properties;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private DataAutomationTemplate template;

    @Autowired
    private D060201Extractor extractor;

    @Autowired
    private D060201Downloader downloader;

    @Autowired
    private D060201Converter converter;

    @Data
    public static class ExtractCondition {
        private List<D060201Extractor.ReportCategory> categories;
        private List<String> stockCodes;
        private Function<D060201Model, Boolean> filter;
    }

    /**
     * 抽取报告记录
     *
     * @param condition
     */
    public void extract(ExtractCondition condition) {
        final List<D060201Extractor.Request> requests = new ArrayList<>();
        final List<D060201Extractor.ReportCategory> categories = condition.getCategories();
        final List<String> stockCodes = condition.getStockCodes();

        // 组装请求参数
        if (Objects.nonNull(categories) && Objects.nonNull(stockCodes)) {
            for (D060201Extractor.ReportCategory category : categories) {
                for (String stockCode : stockCodes) {
                    requests.add(D060201Extractor.Request.create(category, stockCode));
                }
            }
        } else if (Objects.nonNull(categories)) {
            requests.addAll(categories.stream().map(D060201Extractor.Request::create).collect(Collectors.toList()));
        } else if (Objects.nonNull(stockCodes)) {
            requests.addAll(stockCodes.stream().map(D060201Extractor.Request::create).collect(Collectors.toList()));
        }

        if (requests.isEmpty()) {
            return;
        }

        final Function<D060201Model, Boolean> filter = condition.getFilter();
        if (Objects.nonNull(filter)) {
            requests.forEach(v -> v.setFilter(filter));
        }

        // 抽取报告记录
        final List<ListenableFuture> futures = new ArrayList<>();
        requests.forEach(item -> {
            int count = extractor.count(item);
            if (count > 0) {
                int pageSize = item.getPageSize();
                int pages = (count / pageSize) + (count % pageSize == 0 ? 0 : 1);
                for (int i = 1; i <= pages; i++) {
                    D060201Extractor.Request finalRequest = new D060201Extractor.Request();
                    BeanUtils.copyProperties(item, finalRequest);
                    finalRequest.setPageNum(i);
                    futures.add(extractorExecutorService.submit(() -> template.execute(finalRequest)));
                }
            }
        });

        for (ListenableFuture future : futures) {
            try {
                future.get();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 下载-还未下载的报告
     *
     * @param table
     */
    public void downloadForNone(String table) {
        Preconditions.checkState(getTables().contains(table), "不存在的表 -> " + table);
        download(table, D060201Model.DownloadStatus.NONE);
    }

    /**
     * 下载-下载失败的报告
     *
     * @param table
     */
    public void downloadForFailed(String table) {
        Preconditions.checkState(getTables().contains(table), "不存在的表 -> " + table);
        download(table, D060201Model.DownloadStatus.FAILED);
    }

    /**
     * 将PDF转换成HTML,只会进行第一次转换,失败或成功的都不会再次进行转换
     *
     * @param table
     */
    public void convertToHtmlForNone(String table) {
        Preconditions.checkState(getTables().contains(table), "不存在的表 -> " + table);
        convertToHtml(table, D060201Model.HtmlConvertStatus.NONE);
    }

    /**
     * 将PDF转换成TEXT,只会进行第一次转换,失败或成功的都不会再次进行转换
     *
     * @param table
     */
    public void convertToTextForNone(String table) {
        Preconditions.checkState(getTables().contains(table), "不存在的表 -> " + table);
        convertToText(table, D060201Model.TextConvertStatus.SUCCESSFUL);
    }

    /**
     * 获取所有表格
     *
     * @return
     */
    private List<String> getTables() {
        final String reportTablePrefix = properties.getSzse().getReportTablePrefix();
        return mongoTemplate.getCollectionNames().stream().filter(item -> item.startsWith(reportTablePrefix) && !item.contains("null")).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
    }

    /**
     * 下载报告
     *
     * @param table
     * @param downloadStatus
     */
    private void download(String table, D060201Model.DownloadStatus downloadStatus) {
        final File reportStorePath = properties.getSzse().getReportStorePath();
        List<D060201Model> models = mongoTemplate.find(Query.query(Criteria.where("downloadStatus").is(downloadStatus)), D060201Model.class, table);

        final List<ListenableFuture> futures = new ArrayList<>();
        models.forEach(model -> futures.add(downloadExecutorService.submit(
                () -> downloader.download(D060201Downloader.Request.builder().model(model).table(table).storePath(reportStorePath).build())
        )));

        for (ListenableFuture future : futures) {
            try {
                final D060201Downloader.Request request = (D060201Downloader.Request) future.get();
                final D060201Model model = request.getModel();
                log.info("\n----------------" + String.join("-", request.getTable(), model.getStockCode(), model.getStockName(), model.getReportTitle(), "下载状态：" + model.getDownloadStatus()) + "！----------------");
                mongoTemplate.save(model, table);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 将报告转换成HTML
     *
     * @param table
     * @param status
     */
    private void convertToHtml(String table, D060201Model.HtmlConvertStatus status) {
        final File reportStorePath = properties.getSzse().getReportStorePath();
        List<D060201Model> models = mongoTemplate.find(Query.query(Criteria.where("htmlConvertStatus").is(status).and("downloadStatus").is(D060201Model.DownloadStatus.SUCCESSFUL)), D060201Model.class, table);

        final List<ListenableFuture> futures = new ArrayList<>();
        models.forEach(model -> futures.add(convertToHtmlExecutorService.submit(
                () -> converter.convertToHtml(D060201Converter.Request.builder().model(model).table(table).storePath(reportStorePath).build())
        )));

        for (ListenableFuture future : futures) {
            try {
                final D060201Converter.Request request = (D060201Converter.Request) future.get();
                final D060201Model model = request.getModel();
                log.info("\n----------------" + String.join("-", request.getTable(), model.getStockCode(), model.getStockName(), model.getReportTitle(), "转换成HTML状态：" + model.getHtmlConvertStatus()) + "！----------------");
                mongoTemplate.save(request.getModel(), table);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 将报告转换成TEXT
     *
     * @param table
     * @param status
     */
    private void convertToText(String table, D060201Model.TextConvertStatus status) {
        final File reportStorePath = properties.getSzse().getReportStorePath();
        List<D060201Model> models = mongoTemplate.find(Query.query(Criteria.where("textConvertStatus").is(status).and("downloadStatus").is(D060201Model.DownloadStatus.SUCCESSFUL)), D060201Model.class, table);

        final List<ListenableFuture> futures = new ArrayList<>();
        models.forEach(model -> futures.add(convertToTextExecutorService.submit(
                () -> converter.convertToText(D060201Converter.Request.builder().model(model).table(table).storePath(reportStorePath).build())
        )));

        for (ListenableFuture future : futures) {
            try {
                final D060201Converter.Request request = (D060201Converter.Request) future.get();
                final D060201Model model = request.getModel();
                log.info("\n----------------" + String.join("-", request.getTable(), model.getStockCode(), model.getStockName(), model.getReportTitle(), "转换成TEXT状态：" + model.getTextConvertStatus()) + "！----------------");
                mongoTemplate.save(request.getModel(), table);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }
}
